package info.niwota.home.fm;

import info.niwota.home.R;
import instant.openvfs.home.Debug;
import instant.openvfs.home.Preferences;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import x.java.io.File;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class FileBrowseActivity extends ShellActivity implements
		OnClickListener, OnItemClickListener {

	/**
	 * 
	 * a file representing "back" to parent for display purpose only
	 * 
	 */
	private class BackFile extends File {
		private static final long serialVersionUID = 1L;

		private Object icon;

		public BackFile() {
			super("/__"); // path does not really matter so long it is valid
			Resources res = getResources();
			icon = BitmapFactory.decodeResource(res, R.drawable.x_back);
		}

		public String getDisplayName() {
			return "..";
		}

		public Object getIcon() {
			return icon;
		}

		public String getPhoneticName() {
			return "back";
		}

		public String getSortKey() {
			return null;
		}

		public boolean isDirectory() {
			return true;
		}

		public boolean isFile() {
			return false;
		}

		public boolean isRoot() {
			return false;
		}

		public long lastModified() {
			return 0;
		}

		@Override
		public long length() {
			return 0;
		}

	}

	private class DropdownAdapter extends ArrayAdapter<FileItem> {
		LayoutInflater mInflater;

		public DropdownAdapter(Activity ctx, ArrayList<FileItem> list) {
			super(ctx, 0, list);
			mInflater = LayoutInflater.from(ctx);
		}

		@Override
		public View getDropDownView(int position, View convertView,
				ViewGroup parent) {
			return getView(position, convertView, parent, R.layout.list_item2);
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			return getView(position, convertView, parent, R.layout.list_item);
		}

		private View getView(int position, View convertView, ViewGroup parent,
				int res) {
			final FileItem ai = getItem(position);

			ViewHolder holder = null;
			if (convertView == null) {
				convertView = mInflater.inflate(res, parent, false);
				holder = new ViewHolder();
				holder.title = (TextView) convertView
						.findViewById(R.id.list_item_title);
				holder.icon = (ImageView) convertView
						.findViewById(R.id.list_item_icon);
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}
			holder.title.setText(ai.toString());
			IconUtils.setFolderIcon(holder.icon, ai.file);
			//
			return convertView;
		}
	}

	private static class FileItem {
		File file;

		@Override
		public String toString() {
			String n = file.getDisplayName();
			return (n.equals("") ? "v:/" : n);
		}
	}

	private final class Lister implements Callable<File[]> {
		private File[] NONE = new File[0];

		public File[] call() throws Exception {
			if (Debug.DEBUG) {
				Log.d(TAG, "Lister folder: " + selectedFolder);
			}
			File[] fl = selectedFolder.listFiles();
			if (Debug.DEBUG) {
				Log.d(TAG, "Lister folder done. " + fl + " size: "
						+ (fl == null ? 0 : fl.length));
			}
			if (fl == null) {
				fl = NONE;
			}
			return fl;
		}
	}

	private final class ProgressHandler extends Handler {
		private boolean started;

		private void handleEnd() {
			started = false;
			listAdapter.clear();
			//
			File[] childs = null;
			try {
				childs = result.get();
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (Debug.DEBUG) {
				Log.d(TAG, "ProgressHandler childs: " + childs);
			}
			if (childs != null) {
				int len = childs.length;
				if (len > 0) {
					Arrays.sort(childs, comparator);
					for (int i = 0; i < len; i++) {
						File f = childs[i];
						listAdapter.add(f);
					}
				}
			}
			if (!selectedFolder.isRoot()) {
				if (backfile == null) {
					backfile = new BackFile();
				}
				listAdapter.insert(backfile, 0); // parent
			}
		}

		@Override
		public void handleMessage(Message msg) {
			int what = msg.what;
			int p = progress;
			if (Debug.DEBUG) {
				Log.d(TAG, "ProgressHandler folder: " + selectedFolder
						+ " what: " + what + " progress: " + progress);
			}
			if (what == WHAT_START) {
				handler.removeMessages(WHAT_END);
				handler.removeMessages(WHAT_PROGRRESS);
				handler.removeMessages(WHAT_START);
				//
				started = true;
				setProgressBarVisibility(true);
				return;
			}
			if (started) {
				Window w = getWindow();
				w.setFeatureInt(Window.FEATURE_PROGRESS, p * 100);
			}
			if (what == WHAT_END) {
				try {
					handleEnd();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					setProgressBarVisibility(false);
				}
			}
		}

		@Override
		public String toString() {
			return "Message Handler";
		}
	}

	private final class ProgressMonitor implements Runnable {

		private void endProgress() {
			progress = 100;
			handler.sendEmptyMessage(WHAT_END);
		}

		private void initProgress() {
			progress = 0;
		}

		public void run() {
			try {
				if (Debug.DEBUG) {
					Log.d(TAG, "ProgressMonitor folder: " + selectedFolder);
				}
				try {
					if (result != null && !result.isDone()) {
						result.cancel(true);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}

				result = executor.submit(lister);

				initProgress();
				//
				boolean started = false;
				final long timeout = 500;
				final long maxtime = 500 * 2 * 60 * 5; // 5 min
				long elapsed = 0;
				while (!result.isDone() && !result.isCancelled()) {
					try {
						result.get(timeout, TimeUnit.MILLISECONDS);
					} catch (TimeoutException e) {
						// only show progress after at least x ms
						// e.printStackTrace();
						//
						Log.d(TAG, "timeout: " + elapsed);
						if (!started) {
							started = true;
							startProgress();
						}
						elapsed += timeout;
					}
					if (elapsed > maxtime) {
						Log
								.d(TAG, "max time exceeded. canceling... "
										+ elapsed);
						result.cancel(true);
						break;
					}
					sendProgress();
				}
				//
				sendProgress();

				if (Debug.DEBUG) {
					Log
							.d(TAG, "ProgressMonitor done folder: "
									+ selectedFolder);
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				endProgress();
			}
		}

		private void sendProgress() {
			progress += 10;
			progress = (progress < 100 ? progress : 99); // never complete
			//
			handler.sendEmptyMessage(WHAT_PROGRRESS);
		}

		private void startProgress() {
			handler.sendEmptyMessage(WHAT_START);
		}
	}

	private static class ViewHolder {
		ImageView icon;
		TextView title;
	}

	private static final String TAG = "FileChooseActivity";

	public static final String SELECTED_URI = "SELECTED_URI";

	private final static int WHAT_END = 3;

	private final static int WHAT_PROGRRESS = 2;

	private final static int WHAT_START = 1;

	private File backfile;

	protected LinearLayout buttons;

	private final Comparator<File> comparator = new Comparator<File>() {
		public int compare(File file1, File file2) {
			boolean isd1 = file1.isDirectory();
			boolean isd2 = file2.isDirectory();
			if (isd1 && !isd2) {
				return -1;
			}
			if (!isd1 && isd2) {
				return 1;
			}
			String k1 = file1.getSortKey();
			String k2 = file2.getSortKey();
			if (k1 == null) {
				return -1;
			}
			if (k2 == null) {
				return 1;
			}
			return k1.compareTo(k2);
		}
	};

	// private File contextFile;

	private final ExecutorService executor = Executors.newFixedThreadPool(2);

	// protected TextView footer;

	private final ProgressHandler handler = new ProgressHandler();

	private ArrayAdapter<File> listAdapter;

	private final Lister lister = new Lister();

	private int progress;

	private final ProgressMonitor progressMonitor = new ProgressMonitor();

	private Future<File[]> result;

	protected TextView selected;

	private File selectedFile;

	protected File selectedFolder;

	private Spinner spinner;

	private ArrayAdapter<FileItem> spinnerAdapter;

	private ListView listView;

	protected void doBack() {
		try {
			File f = selectedFolder;
			File nf = (x.java.io.File) f.getParentFile();
			//
			if (Debug.DEBUG) {
				Log.d(TAG, "file: " + f + " new: " + nf);
			}
			//
			if (nf != null) {
				f = nf;
			}
			doSelectFolder(nf);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void doSelectFile(File f) {
		try {
			selectedFile = f;
			setSelectedText(f);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void doSelectFolder(File f) {
		try {
			refreshSpinner(f); // will trigger refresh list ?
			setSelectedText(f);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void doItemSelect(AdapterView<?> av, View v, int pos, long id) {
		// spinner selected
		// remove sub folders
		int vid = v.getId();
		int cnt = spinnerAdapter.getCount();
		if (Debug.DEBUG) {
			Log.d(TAG, "****************************** doItemSelect vid:" + vid
					+ " position: " + pos + " id: " + id + " count: " + cnt);
		}
		//
		for (int i = cnt - 1; i > pos; i--) {
			FileItem fi = spinnerAdapter.getItem(i);
			spinnerAdapter.remove(fi);
		}

		FileItem fi = (FileItem) av.getItemAtPosition(pos);
		File f = fi.file;
		selectedFolder = f;
		selectedFile = f;
		//
		refreshList(f);
	}

	private void init() {
		if (Debug.DEBUG) {
			Log.d(TAG, "init");
		}
		ArrayList<File> list = new ArrayList<File>();
		listAdapter = new FileListAdapter(this, list);

		listView = (ListView) findViewById(android.R.id.list);

		listView.setAdapter(listAdapter);
		listView.setOnItemClickListener(this);
		View empty = findViewById(android.R.id.empty);
		listView.setEmptyView(empty);
		// listView.setOnItemSelectedListener(this);

		// setListAdapter(listAdapter);
		//
		ArrayList<FileItem> parents = new ArrayList<FileItem>();
		spinner = (Spinner) findViewById(R.id.filelist_parent);
		// spinnerAdapter = new ArrayAdapter<FileItem>(this,
		// R.layout.choosefile_item, parents);
		spinnerAdapter = new DropdownAdapter(this, parents);
		spinner.setAdapter(spinnerAdapter);
		spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
			public void onItemSelected(AdapterView<?> av, View v, int pos,
					long id) {
				doItemSelect(av, v, pos, id);
			}

			public void onNothingSelected(AdapterView<?> arg0) {
			}
		});
		// footer = (TextView) findViewById(R.id.filelist_footer);

		buttons = (LinearLayout) findViewById(R.id.filelist_buttons);

		Button okBtn = (Button) findViewById(R.id.filelist_confirm);
		Button canBtn = (Button) findViewById(R.id.filelist_cancel);

		okBtn.setOnClickListener(this);
		canBtn.setOnClickListener(this);
		//
		// footer.setOnClickListener(this);
		//
		selected = (TextView) findViewById(R.id.filelist_selected);
		//
		// footer.setVisibility(View.GONE);
		selected.setVisibility(View.GONE);
		buttons.setVisibility(View.VISIBLE);
		//
		ListView lv = getListView();
		registerForContextMenu(lv);
	}

	private ListView getListView() {
		return listView;
	}

	public void onClick(View v) {
		int id = v.getId();
		switch (id) {
		case R.id.filelist_confirm:
			File f = selectedFile;
			doConfirm(f);
			break;
		case R.id.filelist_cancel:
			doCancel();
			break;
		// case R.id.filelist_footer:
		// help();
		// return;
		default:
		}
		finish();
	}

	protected void doCancel() {
	}

	@Override
	protected void onPause() {
		super.onPause();
		File f = selectedFolder;
		savePath(f);
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "onPause: " + f);
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (Debug.DEBUG) {
			Log.d(TAG, "onResume");
		}
	}

	protected void savePath(File f) {
		String p = (f == null ? null : f.getPath());
		if (Debug.DEBUG) {
			Log.d(TAG, "savePath file: " + p);
		}
		Preferences.putLastPath(this, p);
	}

	protected void doConfirm(File f) {
		savePath(f);
		//
		Intent data = new Intent();
		String u = f.toURI().toString();
		data.putExtra(SELECTED_URI, u);
		setResult(RESULT_OK, data);
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreate: " + savedInstanceState);
		}
		// setTheme(android.R.style.Theme_Translucent_NoTitleBar);

		requestWindowFeature(Window.FEATURE_PROGRESS);
		requestWindowFeature(Window.FEATURE_LEFT_ICON);
		//
		setContentView(R.layout.choosefile);

		setTitle(R.string.app_name);
		getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
				R.drawable.openvfs25);
		//
		setProgressBarIndeterminateVisibility(false);
		setProgressBarVisibility(false);

		init();
	}

	protected void onError(Exception e) {
		Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
		e.printStackTrace();
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			doFinish();
			return true;
		} else {
			return super.onKeyDown(keyCode, event);
		}
	}
	
	@Override
	protected void doFinish() {
	}

	protected void onListItemClick(ListView l, View v, int pos, long id) {
		int vid = v.getId();
		if (Debug.DEBUG) {
			Log.d(TAG, "onListItemClick vid:" + vid + " position: " + pos
					+ " id: " + id);
		}
		File f = listAdapter.getItem(pos);
		if (f instanceof BackFile) {
			doBack();
		} else {
			if (f.isFile()) {
				doSelectFile(f);
			} else {
				f.refresh();
				doSelectFolder(f);
			}
		}
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		//
		selectedFile = selectedFolder;
		if (Debug.DEBUG) {
			Log.d(TAG, "onOptionsItemSelected id:" + id);
		}
		//switch (id) {
		//case R.id.home_new_opt_menu_id:
		//	perform(BinConstant.BIN_NEWMENU);
		//	return true;
		//}
		return super.onOptionsItemSelected(item);
	}

	protected void refresh(File newfile) {
		File p = (File) (newfile.isDirectory() ? newfile : newfile
				.getParentFile());
		doSelectFolder(p);
	}

	private void refreshList(File newfile) {
		if (Debug.DEBUG) {
			Thread th = Thread.currentThread();
			Log.d(TAG, "refreshList folder: " + newfile.isDirectory() + " : "
					+ newfile + " thread: " + th);
		}
		if (newfile.isDirectory()) {
			try {
				executor.submit(progressMonitor);
			} catch (Exception e) {
				onError(e);
			} finally {
			}
		}
		if (Debug.DEBUG) {
			Log.d(TAG, "refresh done");
		}
	}

	private void refreshSpinner(File folder) {
		if (Debug.DEBUG) {
			Log.d(TAG, "refreshSpinner newfile:" + folder);
		}
		//
		spinnerAdapter.clear();

		ArrayList<File> parents = new ArrayList<File>();
		File p = folder;
		while (p != null) {
			parents.add(0, p);
			p = (x.java.io.File) p.getParentFile();
		}
		for (File f : parents) {
			FileItem fi = new FileItem();
			fi.file = f;
			spinnerAdapter.add(fi);
		}
		spinner.setSelection(parents.size() - 1);
	}

	protected void setSelectedText(File newfile) {
	}

	@Override
	protected File getSelectedFile() {
		return selectedFile;
	}

	protected void perform(String bin) {
		try {
			perform(bin, selectedFile);
		} catch (IOException e) {
			onError(e);
		}
	}

	@Override
	protected File getContextFile(int pos) {
		File f = listAdapter.getItem(pos);
		if (f instanceof BackFile) {
			f = selectedFolder;
		}
		selectedFile = f;
		return f;
	}

	public void onItemClick(AdapterView<?> adapter, View v, int pos, long id) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onItemClick adapter:  " + adapter + " view: " + v
					+ " pos: " + pos + " id: " + id);
		}
		onListItemClick(listView, v, pos, id);
	}
}
